#include "log_impl.hh"
#include "writer.hh"
#include <thread>

#ifdef WIN32
#	define WIN32_LEAN_AND_MEAN
#	include <windows.h>
#   include <process.h>
#   include <winSock2.h>
#else
#   include <sys/time.h>
#   include <sys/types.h>
#   include <stdio.h>
#   include <string.h>
#   include <errno.h> 
#   include <unistd.h>
#endif // WIN32

namespace kratos {
namespace corelib {
LogImpl::LogImpl(Level level)
    : level_(level), isNull_(false) {
    _getTime(timeInfo_);
}
LogImpl::LogImpl()
    : isNull_(true) {
}
LogImpl::~LogImpl() {
}
void LogImpl::operator<<(const Finish &) {
    if (isNull_) {
        return;
    }
    sstream_ << endl;
    _LoggerRef._write(*this);
    sstream_.str("");
}

std::string LogImpl::str() {
    return sstream_.str();
}

void LogImpl::reset() {
    _getTime(timeInfo_);
    sstream_.str("");
}

#ifdef WIN32
int gettimeofday(struct timeval *tp, void *tzp) {
    (void)tzp;
    time_t clock;
    struct tm tm;
    SYSTEMTIME wtm;
    GetLocalTime(&wtm);
    tm.tm_year = wtm.wYear - 1900;
    tm.tm_mon = wtm.wMonth - 1;
    tm.tm_mday = wtm.wDay;
    tm.tm_hour = wtm.wHour;
    tm.tm_min = wtm.wMinute;
    tm.tm_sec = wtm.wSecond;
    tm.tm_isdst = -1;
    clock = mktime(&tm);
    tp->tv_sec = (long)clock;
    tp->tv_usec = wtm.wMilliseconds * 1000;
    return (0);
}
#endif // WIN32

void LogImpl::_write(info i) {
    switch (i) {
    case info::thread:
        sstream_ << std::this_thread::get_id();
        break;
    case info::process:
    #ifdef WIN32
        sstream_ << ::GetCurrentProcessId();
    #else
        sstream_ << getpid();
    #endif // WIN32
        break;
    case info::year:
        sstream_ << timeInfo_.year;
        break;
    case info::month:
        if (timeInfo_.month < 10) {
            sstream_ << "0";
        }
        sstream_ << timeInfo_.month;
        break;
    case info::day:
        if (timeInfo_.day < 10) {
            sstream_ << "0";
        }
        sstream_ << timeInfo_.day;
        break;
    case info::hour:
        if (timeInfo_.hour < 10) {
            sstream_ << "0";
        }
        sstream_ << timeInfo_.hour;
        break;
    case info::minute:
        if (timeInfo_.minute < 10) {
            sstream_ << "0";
        }
        sstream_ << timeInfo_.minute;
        break;
    case info::second:
        if (timeInfo_.second < 10) {
            sstream_ << "0";
        }
        sstream_ << timeInfo_.second;
        break;
    case info::million:
        if (timeInfo_.million < 10) {
            sstream_ << "00";
        } else if (timeInfo_.million < 100) {
            sstream_ << "0";
        }
        sstream_ << timeInfo_.million;
        break;
    case info::syserror:
        _writeSysError();
        break;
    case info::syserrno:
        _writeSysErrno();
        break;
    default:
        break;
    }
}

void _getTimeval(timeval & tp, tm & t) {
    auto timestamp = time(nullptr);
    gettimeofday(&tp, 0);
#ifdef WIN32
    localtime_s(&t, &timestamp);
#else
    localtime_r(&timestamp, &t);
#endif // WIN32
}

void LogImpl::_getTime(TimeInfo & info) {
    timeval tp;
    tm t;
    _getTimeval(tp, t);
    info.year = t.tm_year + 1900;
    info.month = t.tm_mon + 1;
    info.day = t.tm_mday;
    info.hour = t.tm_hour;
    info.minute = t.tm_min;
    info.second = t.tm_sec;
    info.million = tp.tv_usec / 1000;
}

void LogImpl::_writeSysError() {
#ifdef WIN32
    LPTSTR lpBuffer;
    DWORD error = GetLastError();
    auto result = FormatMessageA(
        FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER,
        NULL,
        error,
        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
        (LPSTR)&lpBuffer,
        0,
        NULL);
    if (result) {
        sstream_ << lpBuffer;
    }
    LocalFree(lpBuffer);
#else
    auto error = strerror(errno);
    if (error) {
        sstream_ << error;
    }
#endif // WIN32
}

void LogImpl::_writeSysErrno() {
#ifdef WIN32
    sstream_ << GetLastError();
#else
    sstream_ << errno;
#endif // WIN32
}

}
}
